import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

//import org.xml.sax.SAXException;


public   class   CitationServlet   extends   HttpServlet   {
	
	private  static final long serialVersionUID = 1L;		
	private  static final String Html   = "HTML";
	private  static final String Text   = "TEXT";	
	private  static final String EndNote = "ENDNOTE";		
	
	private  static final String Filename = "citation"; // citation.html/txt		
	private  static final String CollectionId   = "pid";
	private  static final String XSLDirectory   = "xsl/";
	private  static final String TempDirectory  = "temp/";
	private  static final String ConfDirectory 	= "conf/";
	private  static final String ConfFile	  	= "conf.xml";
	//private  static final String XSLDirectory = "/project/stylesheet/";		
	private  static final String Txt			 = "txt";
	private  static final String Ris			 = "ris";
	private  static final String OutBuffer		 = "cit";
	private  static final String FS = System.getProperty("file.separator");
	
	private final String mail_smtp_debug = "mail.smtp.debug";
	private final String mail_smtp_hostname = "mail.smtp.hostname";
	private final String mail_smtp_password = "mail.smtp.password";
	private final String mail_smtp_port	  = "mail.smtp.port";
	private final String mail_smtp_username = "mail.smtp.username";
	private final String mail_smtp_useSSL	  = "mail.smtp.useSSL";
	private final String mail_transport_protocol = "mail.transport.protocol";
	private final String vital_administrator	 = "vital.administrator";
	
	private  String ServletContext = "";
	private  String Stylesheet    = "";
	private  String OutType	= "html";
	private  String TokenHeader = "";
	private  String TokenEnd = "";
	
	private void clean() {
		this.ServletContext = "";
		this.Stylesheet = "";
		this.TokenHeader = "";
		this.TokenEnd = "";
		this.OutType	= "html";
	}
	
	private void setStyleSheet(HttpServletRequest request) {						
		String xslSuffix	 = ".xsl";			
		String styleDir		 = "harvard";
		if (request.getParameter("format").equalsIgnoreCase(Html))
			Stylesheet = Html.toLowerCase() + xslSuffix;
		else if (request.getParameter("format").equalsIgnoreCase(Text))
			Stylesheet = Text.toLowerCase() + xslSuffix;
		else
			Stylesheet = EndNote.toLowerCase() + xslSuffix;
		if (request.getParameter("style") != null)
			styleDir = request.getParameter("style").toLowerCase();
		
		Stylesheet = ServletContext +
					 XSLDirectory +
					 styleDir +
					 FS +
					 Stylesheet;			
		if (!(new File(Stylesheet)).exists())
			Stylesheet = ServletContext + XSLDirectory+"default.xsl";			
	}
	
	private void configure(HttpServletRequest request) {
		this.setStyleSheet(request);			
		if (request.getParameter("format") != null)
			OutType = request.getParameter("format").toLowerCase();
		if (OutType.equalsIgnoreCase(Text.toLowerCase()))
			OutType = Txt;
		if (OutType.equalsIgnoreCase(EndNote.toLowerCase()))
			OutType = Ris;
		return;
	}
	
	private void configureHeaderEnd() {
		//String LineBreak	= "<BR />";
		String tokenHeader = "<LI>";
		String tokenEnd	= "</LI>";
		
		if (OutType.equalsIgnoreCase(Html)) {					
			TokenHeader = tokenHeader; 			
			TokenEnd	= tokenEnd;
		}
	}
	
	private String matchRequest(HttpServletRequest req, String expr) {
		String element = "";
		for (Enumeration<String> e = req.getParameterNames(); e.hasMoreElements(); ) {
			element = (String) e.nextElement();
			if (element.contains(expr))
				return req.getParameter(element);
		}
		
		return null;
	}
	
	private Hashtable<String, String> getMailConfiguration(String mailConfigFile) throws MP2Exception {
		Hashtable<String, String> mail = new Hashtable<String, String>();
		MP2Exception mp2 = new MP2Exception();
		BufferedReader br;
		try {
			br = new BufferedReader(new FileReader(mailConfigFile));
		} catch (FileNotFoundException e1) {
			mp2.setMessage("Mail Server Configuration File is missing.");
			e1.printStackTrace();
			throw mp2;
		}
		String line;
		String currLine[] = null;
		try {
			while ((line = br.readLine()) != null) {
				if (line.trim().isEmpty() || (line.trim().charAt(0) == '#'))
					continue;
				currLine = line.split("=", 2);
				if (currLine.length != 2)
					continue;
				if (currLine[0].trim().equals(vital_administrator)) {
					mail.put(vital_administrator, currLine[1].trim());
				} else if (currLine[0].trim().equals(mail_smtp_debug)) {
					mail.put(mail_smtp_debug, currLine[1].trim());
				} else if (currLine[0].trim().equals(mail_smtp_hostname)) {
					mail.put(mail_smtp_hostname, currLine[1].trim());
				} else if (currLine[0].trim().equals(mail_smtp_password)) {
					mail.put(mail_smtp_password, currLine[1].trim());
				} else if (currLine[0].trim().equals(mail_smtp_port)) {
					mail.put(mail_smtp_port, currLine[1].trim());
				} else if (currLine[0].trim().equals(mail_smtp_username)) {
					mail.put(mail_smtp_username, currLine[1].trim());
				} else if (currLine[0].trim().equals(mail_smtp_useSSL)) {
					mail.put(mail_smtp_useSSL, currLine[1].trim());
				} else if (currLine[0].trim().equals(mail_transport_protocol)) {
					mail.put(mail_transport_protocol, currLine[1].trim());
				}
			}
		} catch (IOException e) {
			mp2.setMessage("Getting Error When Reading Mail Configuration File.");
			e.printStackTrace();
			throw mp2;
		}
		return mail;
	}
	
	private void sendCitationViaEmail(String file, HttpServletRequest request) throws MP2Exception {
		String address = matchRequest(request, "address");
		String mailConfigs[];
		String mailConfigFile;
		String title;
		String text;
		Hashtable<String, String> mail = new Hashtable<String, String>();
		
		MP2Exception mp2 = new MP2Exception();
		if (address == null) {
			mp2.setMessage("Email address is invalid.");
			mp2.printStackTrace();
			throw mp2;
		}

		try {
			mailConfigs = readMailFromConfig();
			if (mailConfigs == null) {
				mp2.setMessage(MP2Exception.ReadingConfFileErr);
				mp2.printStackTrace();
				throw mp2;
			}
			mailConfigFile = mailConfigs[0];
			title		   = mailConfigs[1];
			text		   = mailConfigs[2];
		}  catch (Exception e) {
			mp2.setMessage(MP2Exception.ReadingConfFileErr);
			e.printStackTrace();
			throw mp2;
		}
		if ((mailConfigFile == null) || !(new File(mailConfigFile).exists())) {
			mp2.setMessage("Mail Server Configuration File is missing.");
			mp2.printStackTrace();
			throw mp2;
		}
		
		mail = getMailConfiguration(mailConfigFile);
		
		Properties mailProps = new Properties();
		mailProps.put(mail_smtp_hostname, mail.get(mail_smtp_hostname));
		Session session = Session.getDefaultInstance(mailProps);
		MimeMessage message = new MimeMessage(session);
		try {
			message.setFrom(new InternetAddress(mail.get(vital_administrator)));
		} catch (Exception e) {
			mp2.setMessage("Getting Error When Setting Mail From Address.");
			e.printStackTrace();
			throw mp2;
		}
		try {
			message.setRecipient(Message.RecipientType.TO, new InternetAddress(address));
		} catch (Exception e) {
			mp2.setMessage("Getting Error When Setting Mail Recipient Address.");
			e.printStackTrace();
			throw mp2;
		}
		try {
			message.setSubject(title);
		} catch (MessagingException e) {
			mp2.setMessage("Getting Error When Setting Mail Subject.");
			e.printStackTrace();
			throw mp2;
		}
		
		try {
			MimeMultipart multi = new MimeMultipart();
			BodyPart textBodyPart = new MimeBodyPart();
			textBodyPart.setText(text);
			multi.addBodyPart(textBodyPart);
			
			BodyPart attachBodyPart = new MimeBodyPart();
			FileDataSource fds = new FileDataSource(file);
			attachBodyPart.setDataHandler(new DataHandler(fds));
			attachBodyPart.setFileName("citation.txt");
			multi.addBodyPart(attachBodyPart);
			
			message.setContent(multi);
			message.saveChanges();
			Transport.send(message);
			
		} catch (MessagingException e) {
			mp2.setMessage("Getting Error When Setting or Sending Mail.");
			e.printStackTrace();
			throw mp2;
		}
	}
	
	private String[] readMailFromConfig() throws ParserConfigurationException, SAXException, IOException {
		String rootTag 			= "module";
		String identifier 		= "name";
		String pointer	  		= "citation";
		Configuration cf = new Configuration(ServletContext + ConfDirectory + ConfFile, 
											 rootTag,
											 identifier,
											 pointer);		
		
		String stops[] = {"mailconf", "mailtitle", "mailtext"};
		String results[] = cf.readFromConfig(stops);
		if (results.length == 0)
			return null;
		return results;
	}
	
	private void requestHandler(HttpServletRequest   request, 
						  		HttpServletResponse   response) {
		ServletContext = getServletContext().getRealPath("") + FS;
		CitationFactory cf = new CitationFactory();
		this.configure(request);
		this.configureHeaderEnd();		
			
		cf.setWebRoot(ServletContext);
		cf.setTempDirectory(ServletContext + TempDirectory);
		cf.setConfFile(ServletContext + ConfDirectory + ConfFile);
		cf.setStyleSheet(Stylesheet);
		cf.addHeader(TokenHeader);
		cf.addFeet(TokenEnd);
		cf.setFileSuffix(OutBuffer);
		cf.cleanTempFile(OutBuffer);
		String outFile = "";
		try {
			cf.readFromConfig();
			outFile = cf.produce(this.getObjects(request));
		} catch (Exception e) {			
			MP2Exception mp2 = new MP2Exception();
			mp2.setMessage(MP2Exception.CitationModuleErr);
			System.err.println(mp2.sendMessage());
			e.printStackTrace();
		}
		if ((matchRequest(request, "save") != null)) {
			try { this.writeResultOut(response, outFile); } 
			catch (Exception e)  {
				MP2Exception mp2 = new MP2Exception();
				mp2.setMessage(MP2Exception.WritingResultErr);
				System.err.println(mp2.sendMessage());
				e.printStackTrace();
			}
		} else {
			try {
				sendCitationViaEmail(outFile, request);
			} catch (MP2Exception e) {
				MP2Exception mp2 = new MP2Exception();
				mp2.setMessage("Getting Error When Sending Email.");
				e.printStackTrace();
			}
			response.setStatus(HttpServletResponse.SC_NO_CONTENT);
		}
		cf.reset();
	}
	
	private void writeResultOut(HttpServletResponse response, String outFile) throws MP2Exception {
		response.setContentType("application/octet-stream");
		response.addHeader("Content-Disposition", "attachment; filename="+Filename+"."+OutType);
		response.addHeader("Content-Type", "application/octet-stream");
		byte[] buffer = new byte[1024];
		int len = 0;
		BufferedInputStream bufferInput = null;
		OutputStream out		= null;
		try {		
			bufferInput = new BufferedInputStream(new FileInputStream(outFile));
			out = response.getOutputStream();
			while ((len = bufferInput.read(buffer)) > 0) {
				out.write(buffer, 0, len);					
			}
			out.flush();
		
			if (bufferInput != null) {
				bufferInput.close();
				bufferInput = null;
			} 
			if (out != null) {
				out.close();
				out = null;
			}
		} catch (Exception e) {
			MP2Exception mp2 = new MP2Exception();
			mp2.setMessage("Receiving Error When Writing Citation Information to the File.");
			System.err.println(mp2.sendMessage());
			e.printStackTrace();
			throw mp2;
		}
	}
	
	private String[] getObjects(HttpServletRequest request) {
		
		String collections = request.getParameter(CollectionId);
		String result[];			
		if (collections.charAt(0) == '[') {
			collections = collections.substring(1, collections.length() - 1);
			result = collections.split(", "); 
		} else {
			result = collections.split(", ");
		}
		return result;
	}
	
	public void init(ServletConfig config) throws ServletException
	{
	    super.init(config);
	}
	
	protected void doPost(HttpServletRequest   request, 
						  HttpServletResponse   response) {		
		this.clean();
		this.requestHandler(request, response);
	}

	protected void doGet(HttpServletRequest   request, 
						 HttpServletResponse   response) {
		this.clean();
		this.requestHandler(request, response);
	}
}